// excerpts from http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#pragma once

#include "TimerQueue.h"
#include "thread/Thread.h"
#include "noncopyable.h"

#include <boost/smart_ptr/scoped_ptr.hpp>
#include <unistd.h>
#include <vector>

#include "TimerId.h"
#include "Callbacks.h"

#include "datetime/Timestamp.h"
#include "thread/Mutex.h"

namespace learnmuduo
{
class Channel;
class Poller;

class EventLoop : public noncopyable {
public:
  typedef boost::function<void()> Functor;

  EventLoop();
  // force out-line dtor, for scoped_ptr members.
  ~EventLoop();

  ///
  /// Loops forever.
  ///
  /// Must be called in the same thread as creation of the object.
  ///
  void loop();

  void quit();

  ///
  /// Time when poll returns, usually means data arrivial.
  ///
  muduo::Timestamp pollReturnTime() const { return pollReturnTime_; }

  /// Runs callback immediately in the loop thread.
  /// It wakes up the loop, and run the cb.
  /// If in the same loop thread, cb is run within the function.
  /// Safe to call from other threads.
  void runInLoop(const Functor& cb);
  /// Queues callback in the loop thread.
  /// Runs after finish pooling.
  /// Safe to call from other threads.
  void queueInLoop(const Functor& cb);

  // timers

  ///
  /// Runs callback at 'time'.
  ///
  TimerId runAt(const muduo::Timestamp& time, const TimerCallback& cb);
  ///
  /// Runs callback after @c delay seconds.
  ///
  TimerId runAfter(double delay, const TimerCallback& cb);
  ///
  /// Runs callback every @c interval seconds.
  ///
  TimerId runEvery(double interval, const TimerCallback &cb);

  // internal use only
  void wakeup();
  void updateChannel(Channel *channel);

  void assertInLoopThread() {
    if (!isInLoopThread()) {
      abortNotInLoopThread();
    }
  }

  bool isInLoopThread() const { return threadId_ == muduo::CurrentThread::tid(); }

private:
  void abortNotInLoopThread();
  void handleRead();  // waked up
  void doPendingFunctors();

  typedef std::vector<Channel*> ChannelList;

  bool looping_; /* atomic */
  bool quit_; /* atomic */
  bool callingPendingFunctors_; /* atomic */
  const pid_t threadId_;
  muduo::Timestamp pollReturnTime_; 
  boost::scoped_ptr<Poller> poller_;
  boost::scoped_ptr<TimerQueue> timerQueue_;
  int wakeupFd_;
  // unlike in TimerQueue, which is an internal class,
  // we don't expose Channel to client.
  boost::scoped_ptr<Channel> wakeupChannel_;
  ChannelList activeChannels_;
  muduo::MutexLock mutex_;
  std::vector<Functor> pendingFunctors_; // @GuardedBy mutex_
};
} // namespace learnmuduo
